#include <iostream>
#include <string>
#include <cstring>
#include <functional>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include "InetAddr.hpp"

using namespace std;

enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    LISTEN_ERROR,
    USAGE_ERROR
};

const static int gbacklog = 16;

class TcpServer;

class ThreadData
{
public:
    ThreadData(int fd, InetAddr addr, TcpServer *s) : sockfd(fd), clientaddr(addr), self(s)
    {   }

    int sockfd;
    InetAddr clientaddr;
    TcpServer* self;
};

// TCP是面向字节流的
class TcpServer
{
public:
    TcpServer(int port) : _port(port), _listensock(-1)
    {   }
    void InitServer()
    {
        // 创建流式套接字
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            cerr << "socket error" << endl;
            exit(SOCKET_ERROR);
        }

        // bind绑定套接字
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;
        int n = bind(_listensock, (struct sockaddr*)&local, sizeof(local));
        if (n < 0)
        {
            cerr << "bind error" << endl;
            exit(BIND_ERROR);
        }

        // 将套接字设置为监听状态
        n = listen(_listensock, gbacklog);
        if (n < 0)
        {
            cerr << "listen error" << endl;
            exit(LISTEN_ERROR);
        }
    }
    // 接收客户端数据并转发给对方
    void Service(int sockfd, InetAddr client)
    {
        string clientaddr = "[" + client.Ip() + ":" + to_string(client.Port()) + "]# ";
        cout << clientaddr << "get a new link, sockfd = " << sockfd << endl;
        while (true)
        {
            char inbuffer[1024];
            ssize_t n = read(sockfd, inbuffer, sizeof(inbuffer) - 1);
            if (n > 0)
            {
                // 从客户端中接收数据
                inbuffer[n] = 0;
                cout << clientaddr << inbuffer << endl;
                // 将数据发送给客户端
                string send_message = "[server echo]# ";
                send_message += inbuffer;
                write(sockfd, send_message.c_str(), send_message.size());
            }
            else if (n == 0)  // 客户端一旦退出，服务器与其建立的连接就会断开，即read返回0
            {
                cout << clientaddr << " quit" << endl;
                break;
            }
            else // read接收数据时发生错误
            {
                cout << "read error" << endl;
                break;
            }
        }
    }
    // 多线程运行函数
    static void* HandlerSock(void* args)
    {
        pthread_detach(pthread_self());
        ThreadData* td = static_cast<ThreadData*>(args);
        td->self->Service(td->sockfd, td->clientaddr);
        delete td;
        return nullptr;
    }
    // TCP通信启动
    void Loop()
    {
        // tcp这里不能直接接受数据，需要先accept获取连接
        while (true) // 死循环设置是为了使多个客户端同时连接服务器
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);

            int sockfd = accept(_listensock, (struct sockaddr*)&peer, &len);
            if (sockfd < 0)
            {
                cout << "accept error, continue accept..." << endl;
                continue;
            }
            //// 版本一: 单进程服务处理，一次只能处理一个客户端请求
            // Service(sockfd, InetAddr(peer));

            //// 版本二: 多进程技术处理，可以一次与多个客户端建立连接
            // pid_t id = fork();
            // if (id == 0) // 子进程
            // {
            //     // 子进程这里关闭文件描述符不会影响父进程，因为进程之间独立
            //     if(fork() > 0)   
            //     {
            //         exit(0);
            //     } 
            //     // 孙子进程执行通信功能。上面创建的进程已经关闭，孙子进程成了孤儿进程，系统自动进程回收
            //     Service(sockfd, InetAddr(peer));
            //     exit(0);
            // }
            // // 注意，这里不能阻塞等待，因为其它客户端还要accept获取连接
            // waitpid(id, nullptr, 0);  
            
            //// 版本三: 采用多线程技术
            pthread_t t;
            ThreadData* td = new ThreadData(sockfd, InetAddr(peer), this);
            pthread_create(&t, nullptr, HandlerSock, td); 
        }
    }
    ~TcpServer()
    {
        if (_listensock > -1) 
        {
            close(_listensock);  // 关闭listen监听套接字
        }
    }

private:
    uint16_t _port;
    int _listensock;
};